package com.jzit168.module_camera

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.os.Environment
import android.util.DisplayMetrics
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import com.alibaba.android.arouter.facade.annotation.Route
import com.jzit168.library_arouter.ARouterConstant
import com.jzit168.library_base.baseVB.BaseVBActivity
import com.jzit168.library_base.ext.requestPermission
import com.jzit168.library_utils.LogUtil
import com.jzit168.library_utils.date2string
import com.jzit168.module_camera.databinding.ActivityCameraBinding
import java.io.File
import java.util.concurrent.Executors
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min

/**
 * @Desc: 相机预览,拍照,录像
 * @Author: lx
 * @Date: 2020/10/27
 *
 */
@Route(path = ARouterConstant.Camera.toCameraActivity)
class CameraActivity : BaseVBActivity<ActivityCameraBinding>() {

    private var lensFacing = CameraSelector.LENS_FACING_BACK
    private val cameraProviderFuture by lazy { ProcessCameraProvider.getInstance(this) }
    private var preview: Preview? = null
    private var imageCapture: ImageCapture? = null
    private var imageAnalyzer: ImageAnalysis? = null
    private var camera: Camera? = null
    private var cameraProvider: ProcessCameraProvider? = null
    private lateinit var outputFile: File

    private var videoCapture: VideoCapture? = null
    private lateinit var videoFile: File

    private val executor by lazy { Executors.newSingleThreadExecutor() }

    private val permissions by lazy { mutableListOf(Manifest.permission.CAMERA) }

    override fun getViewBinding(): ActivityCameraBinding =
        ActivityCameraBinding.inflate(layoutInflater)

    override fun initView(savedInstanceState: Bundle?) {
        super.initView(savedInstanceState)
        requestPermission(
            permissions,
            "需要开启相机权限才能正常拍照",
            "未开启相机权限,请在应用权限管理里开启相机权限"
        ) { allGranted, _, _ ->
            if (allGranted) {
                setCameraUI()
                outputFile = getOutputDirectory(this)
                videoFile = getOutputVideoDir(this)
            } else {
                finish()
            }
        }
    }

    private var mode: Int = 0
    private var isStart = false

    override fun initListener() {
        super.initListener()
        binding.apply {
            group.setOnCheckedChangeListener { group, checkedId ->
                when (checkedId) {
                    radioPicture.id -> {
                        mode = PICTURE
                        takePicture.setImageResource(R.drawable.drawable_take_stated)
                    }
                    radioVideo.id -> {
                        mode = VIDEO
                        takePicture.setImageResource(R.drawable.drawable_video_normal)
                    }
                }
            }
            takePicture.setOnClickListener {
                try {
                    when (mode) {
                        PICTURE -> {
                            takePicture()
                        }
                        VIDEO -> {
                            if (isStart) {
                                isStart = false
                                stopVideo()
                            } else {
                                isStart = true
                                takeVideo()
                            }

                        }
                    }
                } catch (e: Exception) {
                    LogUtil.e(e)
                }
            }
        }
    }

    //初始化CameraX
    private fun setCameraUI() {
        cameraProviderFuture.addListener({
            cameraProvider = cameraProviderFuture.get()
            lensFacing = when {
                hasBackCamera() -> CameraSelector.LENS_FACING_BACK
                hasFrontCamera() -> CameraSelector.LENS_FACING_FRONT
                else -> throw IllegalStateException("Back and Front camera are unavailable")
            }
            //绑定相机使用UseCases
            bindCameraUseCases()
        }, ContextCompat.getMainExecutor(this))
        binding.radioPicture.isChecked = true
        mode = PICTURE
    }

    @SuppressLint("RestrictedApi")
    private fun bindCameraUseCases() {
        val metrics = DisplayMetrics().also { binding.preview.display.getRealMetrics(it) }
        LogUtil.e("Screen metrics:${metrics.widthPixels}--${metrics.heightPixels}")
        val screenAspectRatio = aspectRatio(metrics.widthPixels, metrics.heightPixels)
        LogUtil.e("Preview aspect ratio:$screenAspectRatio")

        val rotation = binding.preview.display.rotation

        val cameraProvider =
            cameraProvider ?: throw IllegalArgumentException("Camera initialization failed")

        val cameraSelector = CameraSelector.Builder().requireLensFacing(lensFacing).build()

        preview = Preview.Builder()
            .setTargetAspectRatio(screenAspectRatio)
            .setTargetRotation(rotation)
            .build()

        imageCapture = ImageCapture.Builder()
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
            .setTargetAspectRatio(screenAspectRatio)
            .setTargetRotation(rotation)
            .build()

        imageAnalyzer = ImageAnalysis.Builder()
            .setTargetAspectRatio(screenAspectRatio)
            .setTargetRotation(rotation)
            .build().also {
                it.setAnalyzer(executor, { luma ->
                    LogUtil.e("Average luminosity:$luma")
                })
            }

        videoCapture = VideoCapture.Builder()
            .setTargetAspectRatio(screenAspectRatio)
            .setTargetRotation(rotation)
            .setVideoFrameRate(30)
            .setBitRate(3 * 1024 * 1024)
            .build()

        cameraProvider.unbindAll()

        try {
            camera = cameraProvider.bindToLifecycle(
                this,
                cameraSelector,
                preview,
                imageCapture,
                videoCapture
            )
            preview?.setSurfaceProvider(binding.preview.surfaceProvider)

        } catch (e: Exception) {
            LogUtil.e("Use case binding failed:${e.message}")
        }

    }

    private fun aspectRatio(width: Int, height: Int): Int {
        val previewRatio = max(width, height).toDouble() / min(width, height)
        if (abs(previewRatio - RATIO_4_3_VALUE) <= abs(previewRatio - RATIO_16_9_VALUE)) {
            return AspectRatio.RATIO_4_3
        }
        return AspectRatio.RATIO_16_9
    }

    //拍照并保存
    private fun takePicture() {
        imageCapture?.let { imageCapture ->
            val photoFile = createFile(outputFile)

            val metadata = ImageCapture.Metadata().apply {
                isReversedHorizontal = lensFacing == CameraSelector.LENS_FACING_FRONT
            }
            val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile)
                .setMetadata(metadata)
                .build()

            imageCapture.takePicture(
                outputOptions,
                executor,
                object : ImageCapture.OnImageSavedCallback {
                    override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                        val saveUri = outputFileResults.savedUri ?: Uri.fromFile(photoFile)
                        LogUtil.e("Photo save Uri:$saveUri")
                    }

                    override fun onError(exception: ImageCaptureException) {
                        LogUtil.e("Photo take failed:${exception.message}")
                    }
                })
        }
    }

    //录像
    @SuppressLint("RestrictedApi")
    private fun takeVideo() {
        videoCapture?.let { videoCapture ->
            val videoFile = createFile(videoFile, VIDEONAME, VIDEO_EXTENSION)

            val metadata = VideoCapture.Metadata()
            val outputOptions = VideoCapture.OutputFileOptions.Builder(videoFile)
                .setMetadata(metadata)
                .build()

            videoCapture.startRecording(
                outputOptions,
                executor,
                object : VideoCapture.OnVideoSavedCallback {
                    override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) {
                        val saveUri = outputFileResults.savedUri ?: Uri.fromFile(videoFile)
                        LogUtil.e("Video save Uri:${saveUri}")
                    }

                    override fun onError(
                        videoCaptureError: Int,
                        message: String,
                        cause: Throwable?
                    ) {
                        LogUtil.e("Video take failed:${cause?.message}----${message}----${videoCaptureError}")
                    }
                })
        }
    }

    @SuppressLint("RestrictedApi")
    private fun stopVideo() {
        videoCapture?.stopRecording()
    }

    //创建图片
    private fun createFile(
        outputFile: File,
        filename: String = FILENAME,
        photoExtension: String = PHOTO_EXTENSION
    ) =
        File(outputFile, "$filename$photoExtension")


    //检测是否有后置摄像头
    private fun hasBackCamera() =
        cameraProvider?.hasCamera(CameraSelector.DEFAULT_BACK_CAMERA) ?: false

    //检测是否有前置摄像头
    private fun hasFrontCamera() =
        cameraProvider?.hasCamera(CameraSelector.DEFAULT_FRONT_CAMERA) ?: false

    private fun getOutputDirectory(context: Context): File {
        val dir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        return if (dir != null && dir.exists()) {
            dir
        } else {
            context.applicationContext.filesDir
        }
    }

    private fun getOutputVideoDir(context: Context): File {
        val dir = context.getExternalFilesDir(Environment.DIRECTORY_DCIM)
        return if (dir != null && dir.exists()) {
            dir
        } else {
            context.applicationContext.filesDir
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        executor.shutdown()
    }

    companion object {
        private const val RATIO_4_3_VALUE = 4.0 / 3.0

        private const val RATIO_16_9_VALUE = 16.0 / 9.0

        private val FILENAME = "photo${System.currentTimeMillis().date2string("yyyy.MM.dd-HH:mm")}"

        private const val PHOTO_EXTENSION = ".jpg"

        private val VIDEONAME = "video${System.currentTimeMillis().date2string("yyyy.MM.dd-HH:mm")}"

        private const val VIDEO_EXTENSION = ".mp4"

        private const val PICTURE = 0

        private const val VIDEO = 1
    }
}